#include "Signal.h"
#include "Source.h"
#include "Sink.h"
#include "plant_1DSPDE_input_1Dsin.h"
#include "plant_1DSOS_RDM_input_1Dsin.h"
#include "Controller.h"
#include "MPC_EW1D_PatternedW_b.h"

// Command line:>> simulation.out JOB_ID REPEAT R2_SET Q_R2 M2_SET Q_M2
// Example: simulation.out 1 10 5000 1.5 1.0 5.0

const int    FREQ = 5;
const int    LATTICE_SIZE = 40000;  // unit: site
const double X_MAX = 1.0*LATTICE_SIZE;      // unit: site

int main(int argc, char** argv){
  char   filename[1024];
  int    job_id = atoi(argv[1]);
  int    repeat = atoi(argv[2]); 
  double r2_set = atof(argv[3]); // unit layer^2, range at t = 1000s, ~[1.4,18]
  double q_r2   = atof(argv[4]);
  double m2_set = atof(argv[5]); // unit dimensionless
  double q_m2   = atof(argv[6]);
  double t_end  = atof(argv[7]);
  double dt     = atof(argv[8]); // unit: (s)econd
  int    mode   = atoi(argv[9]);

  MPC_Formulation* mpc = new MPC1_PatternedW(r2_set,
                                             q_r2,
					     m2_set,
					     q_m2,
					     LATTICE_SIZE,
					     X_MAX,
					     t_end,
					     dt,
					     mode,
					     FREQ);
  controller_MPC_IPOPT* controller = new controller_MPC_IPOPT(6,mode,4,dt);
  controller->load_formulation(mpc);

  // plant1D_SOS_RDM_input_1Dsin* wafer = new plant_1DSOS_RDM_input_1Dsin(dt,LATTICE_SIZE,mode,1.8,1.2,0.0);
  plant1D_SPDE_input_1Dsin* wafer = new plant1D_SPDE_input_1Dsin(mode,LATTICE_SIZE,dt);
  plant_1DSOS_RDM_input_1Dsin* wafer_KMC = new plant_1DSOS_RDM_input_1Dsin(dt,LATTICE_SIZE,mode,1.8,1.2,0.0,false);

  sprintf(filename,"Output_h_%d",job_id);
  block* of_h = new Sink_ToFile(filename,t_end,dt,0);
  sprintf(filename,"Output_h_KMC_%d",job_id);
  block* of_h_KMC = new Sink_ToFile(filename,t_end,dt,0);

  sprintf(filename,"Output_r2_%d",job_id);
  block* of_r2 = new Sink_ToFile(filename,t_end,dt,0);
  sprintf(filename,"Output_r2_KMC_%d",job_id);
  block* of_r2_KMC = new Sink_ToFile(filename,t_end,dt,0);

  sprintf(filename,"Output_m2_%d",job_id);
  block* of_m2 = new Sink_ToFile(filename,t_end,dt,0);
  sprintf(filename,"Output_m2_KMC_%d",job_id);
  block* of_m2_KMC = new Sink_ToFile(filename,t_end,dt,0); 

  sprintf(filename,"Output_corlength_%d",job_id);
  block* of_a = new Sink_ToFile(filename,t_end,dt,0);
  sprintf(filename,"Output_corlength_KMC_%d",job_id);
  block* of_a_KMC = new Sink_ToFile(filename,t_end,dt,0);

  sprintf(filename,"Output_T_%d",job_id);
  block* of_T = new Sink_ToFile(filename,t_end,dt,0);
  sprintf(filename,"Output_W_%d",job_id);
  block* of_W = new Sink_ToFile(filename,t_end,dt,0);
  sprintf(filename,"Output_A_%d",job_id);
  block* of_A = new Sink_ToFile(filename,t_end,dt,0);
  sprintf(filename,"Output_Freq_%d",job_id);
  block* of_Freq = new Sink_ToFile(filename,t_end,dt,0);

  wafer->ConnectInputTo(0,*controller,0); // W0: unit: layer/s
  wafer->ConnectInputTo(1,*controller,1); // A : unit: layer/s
  wafer->ConnectInputTo(2,*controller,2); // k : unit: period within the domain
  wafer->ConnectInputTo(3,*controller,3); // T : unit: K

  wafer_KMC->ConnectInputTo(0,*controller,0); // W0: unit: layer/s
  wafer_KMC->ConnectInputTo(1,*controller,1); // A : unit: layer/s
  wafer_KMC->ConnectInputTo(2,*controller,2); // k : unit: period within the domain
  wafer_KMC->ConnectInputTo(3,*controller,3); // T : unit: K

  of_W->ConnectInputTo(0,*controller,0);
  of_A->ConnectInputTo(0,*controller,1);
  of_Freq->ConnectInputTo(0,*controller,2);
  of_T->ConnectInputTo(0,*controller,3);

  of_W->ConnectInputTo(0,*controller,0);
  of_A->ConnectInputTo(0,*controller,1);
  of_Freq->ConnectInputTo(0,*controller,2);
  of_T->ConnectInputTo(0,*controller,3);

  of_h ->ConnectInputTo(0,*wafer,0);
  of_r2->ConnectInputTo(0,*wafer,1);
  of_m2->ConnectInputTo(0,*wafer,2);
  of_a ->ConnectInputTo(0,*wafer,3);

  of_h_KMC ->ConnectInputTo(0,*wafer_KMC,0);
  of_r2_KMC->ConnectInputTo(0,*wafer_KMC,1);
  of_m2_KMC->ConnectInputTo(0,*wafer_KMC,2);
  of_a_KMC ->ConnectInputTo(0,*wafer_KMC,3);
  
  controller->ConnectInputTo(0,*wafer,4);//connext the no.4 output of wafer to no.0 input of controler
  controller->ConnectInputTo(1,*wafer,5);
  controller->ConnectInputTo(2,*wafer,0); // h: unit, nm
  controller->ConnectInputTo(3,*wafer,1); // r2: unit, nm^2
  controller->ConnectInputTo(4,*wafer,2); // m2: unit, dimensionless
  controller->ConnectInputTo(5,*wafer,3); // a:  unit, nm
  
  for(int repeat_counter=0;repeat_counter<repeat;repeat_counter++){
    cout << "repeat #" << repeat_counter+1 << "\n";
    double sysTime = 0.0;
    while(sysTime<=t_end){
      controller->update(sysTime);
      wafer->update(sysTime);
      wafer_KMC->update(sysTime);
      
      of_h ->update(sysTime);
      of_r2->update(sysTime);
      of_m2->update(sysTime);
      of_a ->update(sysTime);

      of_h_KMC ->update(sysTime);
      of_r2_KMC->update(sysTime);
      of_m2_KMC->update(sysTime);
      of_a_KMC ->update(sysTime);

      of_W ->update(sysTime);
      of_A ->update(sysTime);
      of_Freq ->update(sysTime);
      of_T ->update(sysTime);

      sysTime += dt;
    }
    if(repeat_counter<2){
      sprintf(filename,"snapshot_%d_repeat_%d.dat",job_id,repeat_counter);
      wafer->getSnapshot(filename);
    }

    controller->reset();
    wafer->reset();
    wafer_KMC->reset();

    of_h->reset();
    of_r2->reset();
    of_m2->reset();
    of_a->reset();

    of_h_KMC ->reset();
    of_r2_KMC->reset();
    of_m2_KMC->reset();
    of_a_KMC ->reset();

    of_W->reset();
    of_A->reset();
    of_Freq->reset();
    of_T->reset();
  }
  // 
  delete controller;
  //  delete mpc;
  delete wafer;
  delete of_h;
  delete of_r2;
  delete of_m2;
  delete of_a;
  delete of_h_KMC;
  delete of_r2_KMC;
  delete of_m2_KMC;
  delete of_a_KMC;
  delete of_T;
  delete of_W;
  delete of_A;
  delete of_Freq;

  system("pause");
}
